package com.hw.config.shiro;

import com.hw.config.shiro.jwt.JwtToken;
import com.hw.entity.po.sys.SysPermission;
import com.hw.entity.po.sys.SysRole;
import com.hw.entity.po.sys.SysUser;
import com.hw.service.sys.SysPermissionService;
import com.hw.service.sys.SysRoleService;
import com.hw.service.sys.SysUserService;
import com.hw.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 自定义Realm
 *
 * @author dolyw.com
 * @date 2018/8/30 14:10
 */
@Slf4j
@Service
public class UserRealm extends AuthorizingRealm {
    @Autowired
    SysUserService sysUserService;

    @Autowired
    SysRoleService sysRoleService;

    @Autowired
    SysPermissionService sysPermissionService;

    /**
     * 使用JWT代替原生Token，限定这个realm只能处理JwtToken（不加的话会报错）
     *
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * shiro认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //JwtToken中重写了这个方法了
        String token = (String) authenticationToken.getCredentials();
        // 获得username
        String username = JwtUtils.getClaim(token, "username");

        //用户不存在（这个在登录时不会进入，只有在token校验时才有可能进入）
        if (StringUtils.isBlank(username)) {
            log.error("token中帐号为空");
            throw new AuthenticationException("token校验失败");
        }
        // 校验token，此处抛出的异常会在JwtFilter中的executeLogin()方法里捕获处理
        JwtUtils.verify(token, username);

        // 根据用户名获取用户
        SysUser sysUser = sysUserService.getOne(username);
        if (ObjectUtils.isEmpty(sysUser)) {
            throw new UnknownAccountException("用户名或密码错误");
        }
        if (1 == sysUser.getLocked()) {
            throw new LockedAccountException("该用户已被锁定,暂时无法登录");
        }

        return new SimpleAuthenticationInfo(token, token, getName());
    }

    /**
     * shiro授权
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = JwtUtils.getClaim(principalCollection.getPrimaryPrincipal().toString(), "username");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        // 添加角色
        List<SysRole> roles = sysRoleService.list(username);
        if (CollectionUtils.isNotEmpty(roles)) {
            simpleAuthorizationInfo.addRoles(roles.stream().map(SysRole::getRoleName).collect(Collectors.toList()));

            // 添加权限
            List<SysPermission> permissions = sysPermissionService.list(roles.stream().map(SysRole::getRoleId).collect(Collectors.toList()));
            if (CollectionUtils.isNotEmpty(permissions)) {
                simpleAuthorizationInfo.addStringPermissions(permissions.stream().map(SysPermission::getPermissionName).collect(Collectors.toList()));
            }
        }

        return simpleAuthorizationInfo;
    }
}
